/*
 * Copyright (C) 2015-2019 Alibaba Group Holding Limited
 */

#include "hal_gpio.h"
#include "aos/kernel.h"
#include "pin_mux.h"
#include "fsl_common.h"
#include "board.h"

#define USING_GPIO_FUNCTIONS

#ifdef USING_GPIO_FUNCTIONS

typedef struct {
    uint8_t    inited;
    uint8_t    actual_value;
} hal_gpio_t;

hal_gpio_t hal_gpio_table[PORT_GPIO_SIZE] =
{
    {0, 0}
};

extern gpio_mapping_t gpio_mapping_table[PORT_GPIO_SIZE];

gpio_mapping_t *get_gpio_list(const uint8_t port)
{
    gpio_mapping_t *rc = NULL;
    uint8_t i = 0;

    for (; i < PORT_GPIO_SIZE; i++) {
        if (gpio_mapping_table[i].port == port) {
            rc = &gpio_mapping_table[i];
            break;
        }
    }

    return rc;
}

typedef struct {
    drv_manufacturer_t start;
    enum IRQn          nxp_irqn;
    uint8_t            mounted;
} isr_mapping_node_t;

static isr_mapping_node_t isr_mapping_node[] =
{
    { {GPIO1, 0  }, GPIO1_Combined_0_15_IRQn,  0 },
    { {GPIO1, 16 }, GPIO1_Combined_16_31_IRQn, 0 },
    { {GPIO2, 0  }, GPIO2_Combined_0_15_IRQn,  0 },
    { {GPIO2, 16 }, GPIO2_Combined_16_31_IRQn, 0 },
    { {GPIO3, 0  }, GPIO3_Combined_0_15_IRQn,  0 },
    { {GPIO3, 16 }, GPIO3_Combined_16_31_IRQn, 0 },
    { {GPIO4, 0  }, GPIO4_Combined_0_15_IRQn,  0 },
    { {GPIO4, 16 }, GPIO4_Combined_16_31_IRQn, 0 },
    { {GPIO5, 0  }, GPIO5_Combined_0_15_IRQn,  0 },
    { {GPIO5, 16 }, GPIO5_Combined_16_31_IRQn, 0 },
};

static isr_mapping_node_t *get_isr_mapping(const GPIO_Type *base, const uint8_t pin)
{
    isr_mapping_node_t *rc = NULL;
    const uint8_t table_size = sizeof(isr_mapping_node) / sizeof(isr_mapping_node[0]);
    uint8_t i = 0;
    const uint8_t base_pin = pin<16?0:16;
    for (; i < table_size; i++) {
        if (isr_mapping_node[i].start.base == base && base_pin == isr_mapping_node[i].start.pin) {
            rc = &isr_mapping_node[i];
            break;
        }
    }

    return rc;
}

__attribute__((weak)) void user_gpio_in_user_call(void *base, uint8_t start_pin)
{
    /* to be implement is used in customer */

}

static void gpio_isr_user_call(GPIO_Type *base, const uint8_t start_pin)
{
    GPIO_PortClearInterruptFlags(base, 0xFFFF << start_pin);
    user_gpio_in_user_call(base, start_pin);
}

void GPIO1_Combined_0_15_IRQHandler(void)
{
    krhino_intrpt_enter();
    gpio_isr_user_call(GPIO1, 0);
    krhino_intrpt_exit();
}

void GPIO1_Combined_16_31_IRQHandler(void)
{
    krhino_intrpt_enter();
    gpio_isr_user_call(GPIO1, 16);
    krhino_intrpt_exit();
}

void GPIO2_Combined_0_15_IRQHandler(void)
{
    krhino_intrpt_enter();
    gpio_isr_user_call(GPIO2, 0);
    krhino_intrpt_exit();
}

void GPIO2_Combined_16_31_IRQHandler(void)
{
    krhino_intrpt_enter();
    gpio_isr_user_call(GPIO2, 16);
    krhino_intrpt_exit();
}

void GPIO3_Combined_0_15_IRQHandler(void)
{
    krhino_intrpt_enter();
    gpio_isr_user_call(GPIO3, 0);
    krhino_intrpt_exit();
}

void GPIO3_Combined_16_31_IRQHandler(void)
{
    krhino_intrpt_enter();
    gpio_isr_user_call(GPIO3, 16);
    krhino_intrpt_exit();
}

void GPIO4_Combined_0_15_IRQHandler(void)
{
    krhino_intrpt_enter();
    gpio_isr_user_call(GPIO4, 0);
    krhino_intrpt_exit();
}

void GPIO4_Combined_16_31_IRQHandler(void)
{
    krhino_intrpt_enter();
    gpio_isr_user_call(GPIO4, 16);
    krhino_intrpt_exit();
}

void GPIO5_Combined_0_15_IRQHandler(void)
{
    krhino_intrpt_enter();
    gpio_isr_user_call(GPIO5, 0);
    krhino_intrpt_exit();
}

void GPIO5_Combined_16_31_IRQHandler(void)
{
    krhino_intrpt_enter();
    gpio_isr_user_call(GPIO5, 16);
    krhino_intrpt_exit();
}

int32_t hal_gpio_init(gpio_dev_t *gpio)
{
    int32_t rc = -1;
    gpio_pin_config_t sw_config;

    if (NULL != gpio) {
        gpio_mapping_t *list_node = NULL;

        if (NULL != (list_node = get_gpio_list(gpio->port))) {
            if (0 == hal_gpio_table[gpio->port].inited) {
                switch (gpio->config)
                {
                case IRQ_MODE:{
                    isr_mapping_node_t *isr_mapping = NULL;
                    sw_config.direction = kGPIO_DigitalInput;
                    sw_config.outputLogic = 0;
                    sw_config.interruptMode = kGPIO_IntRisingOrFallingEdge;

                    if (NULL != (isr_mapping = get_isr_mapping(list_node->drv.base, list_node->drv.pin))) {
                        if (0 == isr_mapping->mounted) {
                            EnableIRQ(isr_mapping->nxp_irqn);
                            isr_mapping->mounted = 1;
                        }
                        rc = 0;
                    }
                }

                    break;

                case INPUT_PULL_UP:
                case INPUT_PULL_DOWN:
                    sw_config.direction = kGPIO_DigitalInput;
                    sw_config.outputLogic = 0;
                    rc = 0;
                    break;

                case OUTPUT_PUSH_PULL:
                case OUTPUT_OPEN_DRAIN_NO_PULL:
                case OUTPUT_OPEN_DRAIN_PULL_UP:
                    sw_config.direction = kGPIO_DigitalOutput;
                    if (NULL != gpio->priv) {
                        sw_config.outputLogic = ((gpio_private_t *)gpio->priv)->output_attr.def;
                    }

                    hal_gpio_table[gpio->port].actual_value = sw_config.outputLogic;
                    rc = 0;
                    break;

                default:
                    break;
                }

                if (0 == rc) {
                    GPIO_PinInit(list_node->drv.base, list_node->drv.pin, &sw_config);
                    if (IRQ_MODE == gpio->config) {
                        GPIO_EnableInterrupts(list_node->drv.base, 1U << list_node->drv.pin);
                    }

                    list_node->hal_dev = gpio;
                    hal_gpio_table[gpio->port].inited = 1;
                }
            }

        }
    }

    return rc;
}

int32_t hal_gpio_output_high(gpio_dev_t *gpio)
{
    int32_t rc = -1;
    gpio_mapping_t * gpio_mapping = NULL;

    if (NULL != gpio && NULL != (gpio_mapping = get_gpio_list(gpio->port))) {
        if (1 == hal_gpio_table[gpio->port].inited) {
            hal_gpio_table[gpio->port].actual_value = 1U;
            GPIO_PinWrite(gpio_mapping->drv.base, gpio_mapping->drv.pin, 1U);

            rc = 0;
        }
    }

    return rc;
}

int32_t hal_gpio_output_low(gpio_dev_t *gpio)
{
    int32_t rc = -1;
    gpio_mapping_t * gpio_mapping = NULL;

    if (NULL != gpio && NULL != (gpio_mapping = get_gpio_list(gpio->port))) {
        if (1 == hal_gpio_table[gpio->port].inited) {
            hal_gpio_table[gpio->port].actual_value = 0U;
            GPIO_PinWrite(gpio_mapping->drv.base, gpio_mapping->drv.pin, 0U);

            rc = 0;
        }
    }

    return rc;
}

int32_t hal_gpio_output_toggle(gpio_dev_t *gpio)
{
    int32_t rc = -1;
    gpio_mapping_t * gpio_mapping = NULL;

    if (NULL != gpio && NULL != (gpio_mapping = get_gpio_list(gpio->port))) {
        if (1 == hal_gpio_table[gpio->port].inited) {
            hal_gpio_table[gpio->port].actual_value ^= 1;
            GPIO_PinWrite(gpio_mapping->drv.base, gpio_mapping->drv.pin, hal_gpio_table[gpio->port].actual_value);

            rc = 0;
        }
    }

    return rc;
}

int32_t hal_gpio_input_get(gpio_dev_t *gpio, uint32_t *pin_value)
{
    int32_t rc = -1;
    if (NULL != gpio && NULL != pin_value) {
        gpio_mapping_t *mapping_node = get_gpio_list(gpio->port);
        if (NULL != mapping_node) {
            *pin_value = GPIO_PinRead(mapping_node->drv.base, mapping_node->drv.pin);
            rc = 0;
        }
    }

    return rc;
}

#endif /* USING_GPIO_FUNCTIONS */
